#!/usr/bin/python3
"""
Create an LUKS2 container via `cryptsetup`.

This stage formats the given `device` to be a Linux Unified Key Setup,
LUKS version 2, container and set the key to be `passphrase`. The uuid
of the container must be specified via the coressponding option.

Use the corresponding `org.osbuild.luks2` device to open the container
during build.

Buildhost commands used: `cryptsetup`.
"""


import os
import subprocess
import sys


import osbuild.api


SCHEMA_2 = r"""
"definitions": {
  "pbkdf2": {
    "description": "pbkdf2 PBKDF parameters",
    "type": "object",
    "additionalProperties": false,
    "required": ["method", "iterations"],
    "properties": {
      "method": {
        "enum": ["pbkdf2"]
      },
      "iterations": {
        "description": "Iterations cost",
        "type": "integer",
        "minimum": 1000,
        "maximum": 4294967295
      }
    }
  },
  "argon2i_d": {
    "description": "argon2i and argon2id PBKDF parameters",
    "type": "object",
    "additionalProperties": false,
    "required": ["method", "iterations"],
    "properties": {
      "method": {
        "enum": ["argon2i", "argon2id"]
      },
      "memory": {
        "description": "Memory cost in kilobytes",
        "type": "integer",
        "minimum": 32,
        "maximum": 4194304
      },
      "iterations": {
        "description": "Iterations cost",
        "type": "integer",
        "minimum": 4,
        "maximum": 4294967295
      },
      "parallelism": {
        "description": "Parallel cost",
        "type": "integer",
        "minimum": 1,
        "maximum": 4
      }
    }
  }
},
"devices": {
  "type": "object",
  "additionalProperties": true,
  "required": ["device"],
  "properties": {
    "device": {
      "type": "object",
      "additionalProperties": true
    }
  }
},
"options": {
  "additionalProperties": false,
  "required": ["passphrase", "uuid", "pbkdf"],
  "properties": {
    "passphrase": {
        "description": "Passphrase to use",
        "type": "string"
    },
    "uuid": {
      "description": "UUID for the LUKS device to use",
      "type": "string"
    },
    "cipher": {
      "description": "Cipher to use",
      "type": "string"
    },
    "pbkdf": {
      "description": "Password-Based Key Derivation Function parameters",
      "oneOf": [
        {"$ref": "#/definitions/pbkdf2"},
        {"$ref": "#/definitions/argon2i_d"}
      ]
    },
    "label": {
      "description": "Label to use",
      "type": "string"
    },
    "subsystem": {
      "description": "Additional label",
      "type": "string"
    },
    "sector-size": {
      "description": "Sector size to use",
      "type": "integer"
    }
  }
}
"""


def main(devices, options):
    device = devices["device"]
    passphrase = options["passphrase"]
    device_uuid = options["uuid"]
    pbkdf = options["pbkdf"]
    cipher = options.get("cipher")
    label = options.get("label")
    subsystem = options.get("subsystem", "")
    sector_size = options.get("sector-size")
    path = os.path.join("/dev", device["path"])

    command = [
        "cryptsetup",
        "-q",  # batch mode
        "--uuid", device_uuid,
        "luksFormat",
        "--type", "luks2",
        "--force-password"
    ]

    if cipher:
        command += ["--cipher", cipher]

    if label:
        command += ["--label", label, "--subsystem", subsystem]

    if sector_size:
        command += ["--sector-size", str(sector_size)]

    # password base key derivation function parameters
    command += [
        "--pbkdf", pbkdf["method"],
        "--pbkdf-force-iterations", str(pbkdf["iterations"])
    ]

    memory = pbkdf.get("memory", 32)
    if memory:
        command += ["--pbkdf-memory", str(memory)]

    parallelism = pbkdf.get("parallelism", 1)
    if parallelism:
        command += ["--pbkdf-parallel", str(parallelism)]

    subprocess.run(command + [path],
                   encoding='utf-8', check=True,
                   input=passphrase)


if __name__ == '__main__':
    args = osbuild.api.arguments()
    ret = main(args["devices"], args["options"])
    sys.exit(ret)
